package org.softmed.rest.server.core.http.specific;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.restlet.Application;
import org.restlet.data.Dimension;
import org.restlet.data.Language;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.ReferenceList;
import org.restlet.data.Status;
import org.restlet.resource.Representation;
import org.restlet.resource.Variant;
import org.softmed.rest.config.HttpMethod;
import org.softmed.rest.config.MimeTypeHandler;
import org.softmed.rest.server.core.handler.HandlerProcessParameters;
import org.softmed.rest.server.core.handler.HandlerProcessor;
import org.softmed.rest.server.core.http.AbstractCommonSpecificHttpMethodProcessor;

public class GetProcessor extends AbstractCommonSpecificHttpMethodProcessor
		implements Cloneable {

	private List<Variant> variants = new ArrayList<Variant>();
	MediaType mediaTypeToProcess;

	@Override
	public boolean canProcess(HttpMethod method) {
		return method.getName().equalsIgnoreCase(Method.GET.getName());
	}

	@Override
	public AbstractCommonSpecificHttpMethodProcessor clone() {
		return new GetProcessor();
	}

	@Override
	protected String getMimeTypeToProcess() {
		if (mediaTypeToProcess == null)
			return null;

		return mediaTypeToProcess.getName();
	}

	@Override
	protected boolean isProcessMimeTypeHandler() {
		return true;
	}

	@Override
	protected void preProcessHttpMethod(HttpMethod requestHttpMethod,
			HandlerProcessor instanceHandlerSolver,
			HandlerProcessParameters parameters) {

		// set the list of possible media types for the resource
		List<MimeTypeHandler> mimeTypes = requestHttpMethod
				.getMimeTypeHandlers();
		if (mimeTypes != null)
			for (MimeTypeHandler mimeTypeHandler : mimeTypes) {
				if (mimeTypeHandler.isActive())
					variants.add(new Variant(new MediaType(mimeTypeHandler
							.getMimeType())));
			}

		mediaTypeToProcess = null;
		// determine the requested media type
		mediaTypeToProcess = determineRequiredMediaType();

		// place it in the sequenceParameters for the HttpMethod Handler...can
		// be handy
		if (mediaTypeToProcess != null)
			parameters.getSequenceParameters().put("mime-type",
					mediaTypeToProcess.getName());

	}

	@Override
	protected void postProcessHttpMethod(HttpMethod requestHttpMethod,
			HandlerProcessor instanceHandlerSolver,
			HandlerProcessParameters parameters) {

	}

	private void setupResponseStatus(HandlerProcessParameters parameters) {
		// some code to finish off the get method representation stuff - from
		// RESTlet's Resource class
		Representation selectedRepresentation = parameters.getResponse()
				.getEntity();

		if (selectedRepresentation == null) {
			if ((parameters.getResponse().getStatus() == null)
					|| parameters.getResponse().getStatus().isSuccess()) {
				parameters.getResponse().setStatus(
						Status.CLIENT_ERROR_NOT_FOUND);
			} else {
				// Keep the current status as the developer might prefer a
				// special status like 'method not authorized'.
			}
		} else {
			// The given representation (even if null) must meet the request
			// conditions (if any).
			if (parameters.getRequest().getConditions().hasSome()) {
				Status status = parameters.getRequest().getConditions()
						.getStatus(parameters.getRequest().getMethod(),
								selectedRepresentation);

				if (status != null) {
					parameters.getResponse().setStatus(status);
					parameters.getResponse().setEntity(null);
				}
			}
		}
	}

	@Override
	protected void preProcessMimeType(MimeTypeHandler selectedMimeTypeHandler,
			HttpMethod requestHttpMethod,
			HandlerProcessor instanceHandlerSolver,
			HandlerProcessParameters parameters) {
		// TODO Auto-generated method stub

	}

	@Override
	protected void postProcessMimeType(MimeTypeHandler selectedMimeTypeHandler,
			HttpMethod requestHttpMethod,
			HandlerProcessor instanceHandlerSolver,
			HandlerProcessParameters parameters) {

		setupResponseStatus(parameters);

	}

	protected MediaType determineRequiredMediaType() {

		// The variant that may need to meet the request conditions
		Representation selectedRepresentation = null;

		List<Variant> variants = getVariants();
		if ((variants == null) || (variants.isEmpty())) {
			// Resource not found
			parameters.getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
			getLogger()
					.warning(
							"A resource should normally have at least one variant added by calling getVariants().add() in the constructor");

			// TODO this method is still not implemented and open to discussion
		} else if (isNegotiateContent()) {
			Variant preferredVariant = getPreferredVariant();

			if (preferredVariant == null) {
				// No variant was found matching the client preferences
				parameters.getResponse().setStatus(
						Status.CLIENT_ERROR_NOT_ACCEPTABLE);

				// The list of all variants is transmitted to the client
				ReferenceList refs = new ReferenceList(variants.size());
				for (Variant variant : variants) {
					if (variant.getIdentifier() != null) {
						refs.add(variant.getIdentifier());
					}
				}

				parameters.getResponse()
						.setEntity(refs.getTextRepresentation());
			} else {
				// Set the variant dimensions used for content negotiation
				parameters.getResponse().getDimensions().clear();
				parameters.getResponse().getDimensions().add(
						Dimension.CHARACTER_SET);
				parameters.getResponse().getDimensions()
						.add(Dimension.ENCODING);
				parameters.getResponse().getDimensions()
						.add(Dimension.LANGUAGE);
				parameters.getResponse().getDimensions().add(
						Dimension.MEDIA_TYPE);

				// Set the negotiated representation as response entity
				return preferredVariant.getMediaType();
				// response.setEntity(getRepresentation(preferredVariant));
			}

			// selectedRepresentation = response.getEntity();
		} else {
			if (variants.size() == 1) {
				// response.setEntity(getRepresentation(variants.get(0)));
				return variants.get(0).getMediaType();
				// selectedRepresentation = response.getEntity();
			} else {
				ReferenceList variantRefs = new ReferenceList();

				for (Variant variant : variants) {
					if (variant.getIdentifier() != null) {
						variantRefs.add(variant.getIdentifier());
					} else {
						getLogger()
								.warning(
										"A resource with multiple variants should provide an identifier for each variant when content negotiation is turned off");
					}
				}

				if (variantRefs.size() > 0) {
					// Return the list of variants
					parameters.getResponse().setStatus(
							Status.REDIRECTION_MULTIPLE_CHOICES);
					parameters.getResponse().setEntity(
							variantRefs.getTextRepresentation());
				} else {
					parameters.getResponse().setStatus(
							Status.CLIENT_ERROR_NOT_FOUND);
				}
			}
		}
		return null;

	}

	// need to fix this
	private boolean isNegotiateContent() {
		// TODO Auto-generated method stub
		return true;
	}

	/**
	 * Returns the preferred variant according to the client preferences
	 * specified in the request.
	 * 
	 * @return The preferred variant.
	 */
	public Variant getPreferredVariant() {
		Variant result = null;
		List<Variant> variants = getVariants();

		if ((variants != null) && (!variants.isEmpty())) {
			Language language = null;

			// Compute the preferred variant. Get the default language
			// preference from the Application (if any).
			Object app = parameters.getContext().getAttributes().get("app");

			if (app instanceof Application) {
				language = ((Application) app).getMetadataService()
						.getDefaultLanguage();
			}
			result = parameters.getRequest().getClientInfo()
					.getPreferredVariant(variants, language);

		}

		return result;
	}

	public Logger getLogger() {
		return parameters.getContext().getLogger();
	}

	private List<Variant> getVariants() {
		return variants;
	}

	@Override
	protected void stopProcessingAfterHttpHandler() {
		setupResponseStatus(parameters);

	}

}
